﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using Common.Utilities;

namespace Workflows.Components.TextMining.Collapse
{
    /// <summary>
    /// 
    /// </summary>
    public class OrganizationNameComparer : IAttributeComparer
    {
        private Dictionary<string,string> _SynonymDictionary;
        /// <summary>full name -> synonyms</summary>
        public Dictionary<string,string> SynonymDictionary
        {
            get
            {
                return this._SynonymDictionary;
            }
            set
            {
                this._SynonymDictionary = value;
            }
        }

        private List<string> _StopWords;
        /// <summary></summary>
        public List<string> StopWords
        {
            get
            {
                return this._StopWords;
            }
            set
            {
                this._StopWords = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public OrganizationNameComparer()
        {
            this._SynonymDictionary = new Dictionary<string, string>();
            // university 
            {
                string fullName1 = "university";
                List<string> synonyms1 = new List<string>();
                synonyms1.Add("university");
                synonyms1.Add("univesity");
                synonyms1.Add("universty");
                synonyms1.Add("universit");
                synonyms1.Add("univ");
                synonyms1.Add("univ.");
                synonyms1.Add("u.");
                foreach(string synonym in synonyms1)
                    this._SynonymDictionary.Add(synonym, fullName1);
            }
            // institute
            {
                string fullName2 = "institute";
                List<string> synonyms2 = new List<string>();
                synonyms2.Add("institute");
                synonyms2.Add("inst");
                synonyms2.Add("inst.");
                synonyms2.Add("institution");
                foreach (string synonym in synonyms2)
                    this._SynonymDictionary.Add(synonym, fullName2);
            }
            // school 
            {
                string fullName = "school";
                List<string> synonyms = new List<string>();
                synonyms.Add("school");
                synonyms.Add("sch");
                synonyms.Add("sch.");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // college 
            {
                string fullName = "college";
                List<string> synonyms = new List<string>();
                synonyms.Add("college");
                synonyms.Add("coll");
                synonyms.Add("coll.");
                synonyms.Add("col");
                synonyms.Add("col.");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // society 
            {
                string fullName = "society";
                List<string> synonyms = new List<string>();
                synonyms.Add("society");
                synonyms.Add("soc");
                synonyms.Add("soc.");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // center 
            {
                string fullName = "center";
                List<string> synonyms = new List<string>();
                synonyms.Add("center");
                synonyms.Add("ctr");
                synonyms.Add("ctr.");
                synonyms.Add("ct.");
                synonyms.Add("ct");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // health 
            {
                string fullName = "health";
                List<string> synonyms = new List<string>();
                synonyms.Add("health");
                synonyms.Add("hlth");
                synonyms.Add("hlth.");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // science 
            {
                string fullName = "science";
                List<string> synonyms = new List<string>();
                synonyms.Add("science");
                synonyms.Add("sciences");
                synonyms.Add("sci");
                synonyms.Add("sci.");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // research 
            {
                string fullName = "research";
                List<string> synonyms = new List<string>();
                synonyms.Add("research");
                synonyms.Add("researches");
                synonyms.Add("res");
                synonyms.Add("res.");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // development 
            {
                string fullName = "development";
                List<string> synonyms = new List<string>();
                synonyms.Add("development");
                synonyms.Add("develop");
                synonyms.Add("dev.");
                synonyms.Add("dev");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // medical/medicine 
            {
                string fullName = "medicine";
                List<string> synonyms = new List<string>();
                synonyms.Add("medical");
                synonyms.Add("med");
                synonyms.Add("med.");
                synonyms.Add("medicine");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // state 
            {
                string fullName = "state";
                List<string> synonyms = new List<string>();
                synonyms.Add("state");
                synonyms.Add("st");
                synonyms.Add("st.");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // department 
            {
                string fullName = "department";
                List<string> synonyms = new List<string>();
                synonyms.Add("department");
                synonyms.Add("dept");
                synonyms.Add("dept.");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // company 
            {
                string fullName3 = "inc";
                List<string> synonyms3 = new List<string>();
                synonyms3.Add("inc");
                synonyms3.Add("inc.");
                synonyms3.Add("incorporated");
                synonyms3.Add("llc");
                synonyms3.Add("llc.");
                synonyms3.Add("co.");
                synonyms3.Add("co");
                synonyms3.Add("corp");
                synonyms3.Add("corp.");
                foreach (string synonym in synonyms3)
                    this._SynonymDictionary.Add(synonym, fullName3);
            }
            // foundation 
            {
                string fullName = "foundation";
                List<string> synonyms4 = new List<string>();
                synonyms4.Add("foundation");
                synonyms4.Add("fdn");
                synonyms4.Add("fdn.");
                foreach (string synonym in synonyms4)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            // and 
            {
                string fullName = "and";
                List<string> synonyms=new List<string>();
                synonyms.Add("and");
                synonyms.Add("&");
                foreach(string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            this._StopWords=new List<string>();
            this._StopWords.Add("the");
            this._StopWords.Add(",");
            this._StopWords.Add("of");
            this._StopWords.Add("for");
            this._StopWords.Add("-");
            this._StopWords.Add(".");
            this._StopWords.Add("at");
            this._StopWords.Add("a");
            this._StopWords.Add("an");
            this._StopWords.Add("\\");
            this._StopWords.Add("/");
            this._StopWords.Add("@");
            this._StopWords.Add("#");
            this._StopWords.Add("_");
            this._StopWords.Add("'");
            this._StopWords.Add(":");
            this._StopWords.Add(";");
        }

        public AttributeComparerType ComparerType
        {
            get { return AttributeComparerType.ClassComparer; }
        }

        /// <summary>
        /// 
        /// </summary>
        public SqlDbType FieldDbType
        {
            get { return SqlDbType.NVarChar; }
            set { throw new NotImplementedException(); }
        }

        public double Compare(object value1, object value2, double penalty, SqlConnection conn,
                              string unevenDistTableName)
        {
            if (value1 == null || value2 == null || value1 == DBNull.Value || value2 == DBNull.Value || 
                value1.ToString().Length == 0 || value2.ToString().Length == 0)
                return 0.0;
            string name1 = value1.ToString().ToLower().Trim();
            string name2 = value2.ToString().ToLower().Trim();
            if (name1 == name2)
                return 1.0;

            string[] words1 = name1.Split(new char[] {' ', ',', '-', '.', '/', '\\'});
            List<string> wordList1=new List<string>();
            foreach(string word in words1)
            {
                if(!string.IsNullOrEmpty(word) && word.Length>0 &&
                    !this._StopWords.Contains(word))
                {
                    if(this._SynonymDictionary.ContainsKey(word))
                        wordList1.Add(this._SynonymDictionary[word]);
                    else 
                        wordList1.Add(word);
                }
            }
            words1 = wordList1.ToArray();
            string[] words2 = name2.Split(new char[] { ' ', ',', '-', '.', '/', '\\' });
            List<string> wordList2 = new List<string>();
            foreach (string word in words2)
            {
                if (!string.IsNullOrEmpty(word) && word.Length>0 && 
                    !this._StopWords.Contains(word))
                {
                    if (this._SynonymDictionary.ContainsKey(word))
                        wordList2.Add(this._SynonymDictionary[word]);
                    else
                        wordList2.Add(word);
                }
            }
            words2 = wordList2.ToArray();
            List<string> commonWords = StringUtil.Intersection(wordList1, wordList2, 1, 1);
            List<string> commonWordList = new List<string>();
            
            if (commonWords != null && commonWords.Count > 0)
            {
                foreach (string commonWord in commonWords)
                {
                    if (!this._SynonymDictionary.ContainsKey(commonWord) && !this._StopWords.Contains(commonWord))
                        commonWordList.Add(commonWord);
                }
            }
            double compareScore = 0.0;
            if (commonWordList != null && commonWordList.Count > 0)
            {
                string[] allWords = StringUtil.Unions(words1, words2);
                List<string> allWordList = new List<string>();
                foreach(string word in allWords)
                {
                    if(!this._SynonymDictionary.ContainsKey(word) && 
                        !this._StopWords.Contains(word))
                        allWordList.Add(word);
                }
                double score = ((double)commonWordList.Count) / allWordList.Count;
                int commonSubSeqLen = StringUtil.LongestCommonSubsequence(name1, name2);
                double score2 = ((double) commonSubSeqLen)/Math.Max(name1.Length, name2.Length);
                int commSubStrLen = StringUtil.LongestCommonSubString(name1, name2);
                double score3 = ((double) commSubStrLen)/Math.Max(name1.Length, name2.Length);
                score = (score3 + score2 + score)/3.0;
                if (score >= 0.5)
                    compareScore = score;
            }
            else
            {
                if (name1.Length < name2.Length
                        && this.IsAcronym(value1.ToString()))
                {
                    if (this.MatchesAcronym(name1, name2))
                        compareScore = 0.65;
                }
                else if (name2.Length < name1.Length &&
                        this.IsAcronym(value2.ToString()))
                {
                    if (this.MatchesAcronym(name2, name1))
                        compareScore = 0.65;
                }
            }
            if (compareScore == 0)
                compareScore = penalty;
            return compareScore;
        }

        public object Merge(List<object> values, SqlConnection conn, string unevenDistTableName)
        {
            if (values == null || values.Count == 0)
                return null;

            Dictionary<object, int> countByValues = new Dictionary<object, int>();
            foreach (object obj in values)
            {
                if (obj == null || obj.ToString().Length==0)
                    continue;

                if (!countByValues.ContainsKey(obj))
                    countByValues.Add(obj, 1);
                else
                    countByValues[obj] = countByValues[obj] + 1;
            }

            if (countByValues.Count > 0)
            {
                object mode = null;
                int count = 0;
                foreach (object obj in countByValues.Keys)
                {
                    if (countByValues[obj] > count)
                    {
                        count = countByValues[obj];
                        mode = obj;
                    }
                }
                return mode;
            }
            else
                return null;
        }

        private bool IsAcronym(string input)
        {
            if(string.IsNullOrEmpty(input) || input.Length >8)
                return false;
            if(input.IndexOf(" ")>0)
                return false;
            char[] chars = input.ToCharArray();
            foreach(char c in chars)
                if(char.IsDigit(c) || char.IsPunctuation(c) || char.IsNumber(c) ||
                    char.IsSeparator(c) || char.IsWhiteSpace(c) || char.IsSymbol(c) ||
                    char.IsLower(c))
                    return false;
            return true;
        }

        private bool MatchesAcronym(string acronym, string input)
        {
            List<string> stopWords = new List<string>();
            stopWords.Add("of");
            stopWords.Add("the");
            stopWords.Add("and");
            stopWords.Add("at");
            stopWords.Add("a");
            stopWords.Add("an");
            stopWords.Add("to");
            stopWords.Add("&");
            string[] words = StringUtil.Split(input.ToLower(), " ");
            char[] letters = acronym.ToLower().ToCharArray();
            List<string> wordList=new List<string>();
            foreach(string word in words)
            {
                if(!stopWords.Contains(word))
                    wordList.Add(word);
            }
            if(wordList.Count !=letters.Length)
                return false;
            for(int i=0;i<letters.Length;i++)
            {
                if(!wordList[i].StartsWith(letters[i].ToString()))
                    return false;
            }
            return true;
        }
    }
}
